// Copyright (C) 1953-2022 NUDT
// Verilog module name - clock_correct_calculate
// Version: V4.1.0.20221206
// Created:
//         by - fenglin
////////////////////////////////////////////////////////////////////////////
// Description:
//         
///////////////////////////////////////////////////////////////////////////

`timescale 1ns/1ps

module clock_correct_calculate
(
    i_clk  ,
    i_rst_n,
	
    iv_gm_timestamps                      ,
	iv_correctfield_time                  ,
	iv_local_cnt_rx                       ,
	i_time_info_valid                     ,
	
	iv_local_count                        ,
	iv_offset_threshold                   ,
	iv_sync_clk                           ,
	
	ov_clk_set                            ,
	o_clk_set_wr                          ,
	
	o_sync_ok                             ,
    o_sync_ok_wr                          ,	
    ov_offset                             ,
	o_offset_wr                           ,
    ov_offset_abnormal_cnt                ,	
	o_sync_result_wr                      ,
    	
);

// I/O
// clk & rst
input                   i_clk;
input                   i_rst_n; 
// pkt input                      
input	   [79:0]	    iv_gm_timestamps        ;
input	   [63:0]	    iv_correctfield_time     ;
input      [23:0]       iv_local_cnt_rx         ;
input                   i_time_info_valid       ;

input      [23:0]       iv_local_count          ;
input      [11:0]       iv_offset_threshold          ;
input      [79:0]       iv_sync_clk          ;
// pkt output to NMA
output reg [79:0]	    ov_clk_set        ;
output reg	            o_clk_set_wr      ;

output reg      	    o_sync_ok        ;
output reg              o_sync_ok_wr ;
output reg [12:0]	    ov_offset    ;
output reg              o_offset_wr  ;
output reg [15:0]	    ov_offset_abnormal_cnt         ;
output reg	            o_sync_result_wr       ;     
//***************************************************
//                 calculate
//***************************************************  
always @(posedge i_clk or negedge i_rst_n) begin
    if(i_rst_n == 1'b0)begin
        ov_clk_set         <= 80'b0;
		o_clk_set_wr       <= 1'b0 ;
    end
    else begin
        if(i_time_info_valid)begin
            if(iv_local_count > iv_local_cnt_rx)begin//本地计数器未计满    
                if(iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count - iv_local_cnt_rx) + 8 < 32'd1000000000)begin//判断以ns为单位的数值计算后是否超过1s，即是否需要向高位进1.
                    ov_clk_set[31:0]         <= iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count - iv_local_cnt_rx) + 8;//时钟校正值的小数位;8ns为计算校正值的延迟
                    ov_clk_set[79:32]        <= iv_gm_timestamps[79:32];//时钟校正值的整数位;以ns为单位的数值无需进位.
                    o_clk_set_wr             <= 1'b1 ;
                end
                else begin
                    ov_clk_set[31:0]         <= iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count - iv_local_cnt_rx) + 8 - 32'd1000000000;//时钟校正值的小数位;8ns为计算校正值的延迟
                    ov_clk_set[79:32]        <= iv_gm_timestamps[79:32] + 1'b1;//时钟校正值的整数位;以ns为单位的数值需进位.
                    o_clk_set_wr             <= 1'b1 ;						
                end
            end
            else begin//本地计数器计满
                if(iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count + 24'hffffff - iv_local_cnt_rx) + 8 < 32'd1000000000)begin//判断以ns为单位的数值计算后是否超过1s，即是否需要向高位进1.
                    ov_clk_set[31:0]         <= iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count + 24'hffffff - iv_local_cnt_rx) + 8;//时钟校正值的小数位;8ns为计算校正值的延迟
                    ov_clk_set[79:32]        <= iv_gm_timestamps[79:32];//时钟校正值的整数位;以ns为单位的数值无需进位.
                    o_clk_set_wr             <= 1'b1 ;
                end
                else begin
                    ov_clk_set[31:0]         <= iv_gm_timestamps[31:0] + iv_correctfield_time[63:16] + (iv_local_count + 24'hffffff - iv_local_cnt_rx) + 8 - 32'd1000000000;//时钟校正值的小数位;8ns为计算校正值的延迟
                    ov_clk_set[79:32]        <= iv_gm_timestamps[79:32] + 1'b1;//时钟校正值的整数位;以ns为单位的数值需进位.
                    o_clk_set_wr             <= 1'b1 ;						
                end            
            end
		end
		else begin
			ov_clk_set         <= 80'b0;
			o_clk_set_wr       <= 1'b0 ;		
		end
    end
end

always @(posedge i_clk or negedge i_rst_n) begin
    if(i_rst_n == 1'b0)begin
		ov_offset          <= 13'b0;
		o_offset_wr        <= 1'b0 ;
    end
    else begin
        if(o_clk_set_wr)begin
		    if(ov_clk_set[79:32] > iv_sync_clk[79:32])begin//+
			    o_offset_wr              <= 1'b1;
				ov_offset[12]            <= 1'b0;   //+
				if(ov_clk_set[79:32] - iv_sync_clk[79:32] > 1)begin
					ov_offset[11:0]          <= 12'hfff;
			    end
				else begin//ov_clk_set[79:32] - iv_sync_clk[79:32] == 1
					if(ov_clk_set[31:0] > iv_sync_clk[31:0])begin
						ov_offset[11:0]          <= 12'hfff;
					end
					else begin
						if(ov_clk_set[31:0] + 32'd1000000000 - iv_sync_clk[31:0] >= 32'hfff)begin
							ov_offset[11:0]          <= 12'hfff;
						end
						else begin
							ov_offset[11:0]          <= ov_clk_set[31:0] + 32'd1000000000 - iv_sync_clk[31:0];
						end					
					end 
				end
            end
			else if(ov_clk_set[79:32] == iv_sync_clk[79:32])begin
				o_offset_wr              <= 1'b1;
				if(ov_clk_set[31:0] >= iv_sync_clk[31:0])begin
					ov_offset[12]            <= 1'b0;   //+
					if(ov_clk_set[31:0] - iv_sync_clk[31:0] >= 32'hfff)begin    
						ov_offset[11:0]          <= 12'hfff;
					end
					else begin
					    ov_offset[11:0]          <= ov_clk_set[31:0] - iv_sync_clk[31:0];
					end
				end
				else begin
				    ov_offset[12]            <= 1'b1;   //-
					if(iv_sync_clk[31:0] - ov_clk_set[31:0] >= 32'hfff)begin    
						ov_offset[11:0]          <= 12'hfff;
					end
					else begin
					    ov_offset[11:0]          <= iv_sync_clk[31:0] - ov_clk_set[31:0];
					end
				end					
            end
            else if(ov_clk_set[79:32] < iv_sync_clk[79:32])begin//-
			    o_offset_wr              <= 1'b1;
				ov_offset[12]            <= 1'b1;   //-
				if(iv_sync_clk[79:32] - ov_clk_set[79:32] > 1)begin
					ov_offset[11:0]          <= 12'hfff;
			    end
				else begin//iv_sync_clk[79:32] - ov_clk_set[79:32] == 1
					if(iv_sync_clk[31:0] > ov_clk_set[31:0])begin
						ov_offset[11:0]          <= 12'hfff;
					end
					else begin
						if(iv_sync_clk[31:0] + 32'd1000000000 - ov_clk_set[31:0] >= 32'hfff)begin
							ov_offset[11:0]          <= 12'hfff;
						end
						else begin
							ov_offset[11:0]          <= iv_sync_clk[31:0] + 32'd1000000000 - ov_clk_set[31:0];
						end					
					end 
				end
            end
            else begin
                o_offset_wr    <= 1'b0;
				ov_offset      <= ov_offset;
            end			
		end
		else begin
			o_offset_wr    <= 1'b0;
			ov_offset      <= ov_offset;		
		end
    end
end

always @(posedge i_clk or negedge i_rst_n) begin
    if(i_rst_n == 1'b0)begin
		o_sync_ok          <= 1'b0 ;
        o_sync_ok_wr       <= 1'b0 ;
    end
    else begin
        if(o_offset_wr)begin
		    o_sync_ok_wr       <= 1'b1 ;
			if(ov_offset[11:0] > iv_offset_threshold)begin
			    o_sync_ok <= 1'b0;
			end
            else begin
                o_sync_ok <= 1'b1;
            end			
		end
		else begin
			o_sync_ok_wr       <= 1'b0 ;
			o_sync_ok          <= o_sync_ok ;	
		end
    end
end
reg      r_sync_ok;
always @(posedge i_clk or negedge i_rst_n) begin
    if(i_rst_n == 1'b0)begin
		r_sync_ok              <= 1'b0;
		ov_offset_abnormal_cnt <= 16'b0;
		o_sync_result_wr       <= 1'b0;
    end
    else begin
	    r_sync_ok <= o_sync_ok;
        if(o_sync_ok_wr)begin
		    o_sync_result_wr   <= 1'b1 ;
			if((r_sync_ok == 1'b1) && (o_sync_ok == 1'b0))begin
			    ov_offset_abnormal_cnt <= ov_offset_abnormal_cnt + 1'b1;
			end
            else begin
                ov_offset_abnormal_cnt <= ov_offset_abnormal_cnt;
            end			
		end
		else begin
			o_sync_result_wr       <= 1'b0;
			ov_offset_abnormal_cnt <= ov_offset_abnormal_cnt;
		end
    end
end
endmodule